// 执行入口, 初始化数据库等配置信息

package main

import (
	"api/common"
	"api/common/logger"
	"api/common/orm"
	"api/models"
	"api/src/api"
	"api/src/basic"
	"api/src/info"
	"api/src/student"
	"bytes"
	"fmt"
	"github.com/go-redis/redis"
	_ "github.com/go-sql-driver/mysql"
	"github.com/kardianos/service"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/pprof"
	"os"
	"strings"
	"time"
)

// 初始化路由
func initRoute() []models.Resource {
	/*系统控制器*/
	apiFormController := new(api.FormController)
	apiHomeController := new(api.HomeController)
	apiMailController := new(api.MailController)
	apiTeacherController := new(api.TeacherController)
	apiUserController := new(api.UserController)
	basicGlobalSetController := new(basic.GlobalSetController)
	basicHomeController := new(basic.HomeController)
	basicResourceController := new(basic.ResourceController)
	basicRoleController := new(basic.RoleController)
	basicSpecialController := new(basic.SpecialController)
	basicUserController := new(basic.UserController)
	infoContentController := new(info.ContentController)
	infoImageController := new(info.ImageController)
	infoNoticeController := new(info.NoticeController)
	studentCountController := new(student.CountController)
	studentGatherController := new(student.GatherController)
	studentRecommendController := new(student.RecommendController)
	studentSumController := new(student.SumController)
	studentVerifyController := new(student.VerifyController)
	/*系统控制器*/

	initResources := []models.Resource{
		/*系统资源*/
		{"000_000", "api", "开设专业列表数据", "api/form/specialList", "POST", apiFormController.SpecialList, ``, ""},
		{"000_001", "api", "是否显示“免冠证件照”上传按钮", "api/form/showPictureUpload", "POST", apiFormController.ShowPictureUpload, ``, ""},
		{"001_000", "api", "首页数据", "api/home/data", "POST", apiHomeController.Data, ``, ""},
		{"001_001", "api", "内容详情数据", "api/home/contentDetail", "POST", apiHomeController.ContentDetail, `{"id":0}`, ""},
		{"001_002", "api", "通知公告详情数据", "api/home/noticeDetail", "POST", apiHomeController.NoticeDetail, `{"id":0}`, ""},
		{"002_000", "api", "发送邮件", "api/mail/send", "POST", apiMailController.Send, `{"mailTo":"", "title":"", "content":""}`, ""},
		{"003_000", "api", "提交用户报名信息(教师代报)", "api/teacher/submit", "POST", apiTeacherController.Submit, `{"data":{}}`, ""},
		{"003_001", "api", "上传用户相关图片(教师代报)", "api/teacher/upload", "POST", apiTeacherController.Upload, `{"b64":"base64Code", "b64sm":"base64Code", "imgType":"jpg", "fieldName":"picture"}`, ""},
		{"004_000", "api", "微信用户登录", "api/user/login", "POST", apiUserController.Login, `{"code":"","nickName":"","userLogo":"","address":""}`, ""},
		{"004_001", "api", "获取用户个人信息", "api/user/info", "POST", apiUserController.Info, `{"userId":0, "token":""}`, ""},
		{"004_002", "api", "提交用户报名信息", "api/user/submit", "POST", apiUserController.Submit, `{"userId":0, "token":"", "data":{}}`, ""},
		{"004_003", "api", "保存用户报名信息", "api/user/save", "POST", apiUserController.Save, `{"userId":0, "token":"", "data":{}}`, ""},
		{"004_004", "api", "上传用户相关图片", "api/user/upload", "POST", apiUserController.Upload, `{"userId":0, "token":"", "b64":"base64Code", "b64sm":"base64Code", "imgType":"jpg", "fieldName":"picture"}`, ""},
		{"005_000", "basic", "全局设置列表数据", "basic/globalSet/list", "POST", basicGlobalSetController.List, ``, ""},
		{"005_001", "basic", "新增全局设置信息", "basic/globalSet/create", "POST", basicGlobalSetController.Create, ``, ""},
		{"005_002", "basic", "编辑全局设置信息", "basic/globalSet/edit", "POST", basicGlobalSetController.Edit, ``, ""},
		{"005_003", "basic", "删除全局设置信息", "basic/globalSet/delete", "POST", basicGlobalSetController.Delete, ``, ""},
		{"006_000", "basic", "后台首页数据", "basic/home/data", "POST", basicHomeController.Data, `{"year":23}`, ""},
		{"007_000", "basic", "资源列表数据", "basic/resource/list", "POST", basicResourceController.List, ``, ""},
		{"008_000", "basic", "角色列表数据", "basic/role/list", "POST", basicRoleController.List, `{"getOtherData":true}`, ""},
		{"008_001", "basic", "新增角色信息", "basic/role/create", "POST", basicRoleController.Create, ``, ""},
		{"008_002", "basic", "编辑角色信息", "basic/role/edit", "POST", basicRoleController.Edit, ``, ""},
		{"008_003", "basic", "删除角色信息", "basic/role/delete", "POST", basicRoleController.Delete, ``, ""},
		{"009_000", "basic", "专业列表数据", "basic/special/list", "POST", basicSpecialController.List, ``, ""},
		{"009_001", "basic", "专业下拉列表框数据", "basic/special/listSelect", "POST", basicSpecialController.ListSelect, ``, ""},
		{"009_002", "basic", "新增专业信息", "basic/special/create", "POST", basicSpecialController.Create, ``, ""},
		{"009_003", "basic", "编辑专业信息", "basic/special/edit", "POST", basicSpecialController.Edit, ``, ""},
		{"009_004", "basic", "删除专业信息", "basic/special/delete", "POST", basicSpecialController.Delete, ``, ""},
		{"00a_000", "basic", "用户列表数据", "basic/user/list", "POST", basicUserController.List, ``, ""},
		{"00a_001", "basic", "新增用户信息", "basic/user/create", "POST", basicUserController.Create, ``, ""},
		{"00a_002", "basic", "编辑用户信息", "basic/user/edit", "POST", basicUserController.Edit, ``, ""},
		{"00a_003", "basic", "删除用户信息", "basic/user/delete", "POST", basicUserController.Delete, ``, ""},
		{"00a_004", "basic", "用户登录", "basic/user/login", "POST", basicUserController.Login, `{"userName":"admin","passwd":"e10adc3949ba59abbe56e057f20f883e"}`, "userName为用户名，passwd为用户密码md5加密值，返回登录状态，登录成功则返回用户可见菜单"},
		{"00a_005", "basic", "修改密码", "basic/user/changPasswd", "POST", basicUserController.ChangPasswd, `{"userId":1,"token":"1","oldPasswd":"","newPasswd":""}`, "修改用户密码，oldPasswd 为原密码，newPasswd为新密码"},
		{"00a_006", "basic", "系部列表数据", "basic/user/dept", "POST", basicUserController.Dept, ``, ""},
		{"00b_000", "info", "文章列表数据", "info/content/list", "POST", infoContentController.List, ``, ""},
		{"00b_001", "info", "新增文章信息", "info/content/create", "POST", infoContentController.Create, ``, ""},
		{"00b_002", "info", "编辑文章信息", "info/content/edit", "POST", infoContentController.Edit, ``, ""},
		{"00b_003", "info", "删除文章信息", "info/content/delete", "POST", infoContentController.Delete, ``, ""},
		{"00b_004", "info", "文章信息回显", "info/content/echo", "POST", infoContentController.Echo, ``, ""},
		{"00b_005", "info", "上传封面图片或文章内容图片", "info/content/upload", "POST", infoContentController.Upload, `{"userId":1,"token":"a","b64":"base64Code","b64sm":"base64Code","imgType":"jpg","contentId":1}`, ""},
		{"00c_000", "info", "轮播图列表数据", "info/image/list", "POST", infoImageController.List, ``, ""},
		{"00c_001", "info", "新增轮播图信息", "info/image/create", "POST", infoImageController.Create, ``, ""},
		{"00c_002", "info", "编辑轮播图信息", "info/image/edit", "POST", infoImageController.Edit, ``, ""},
		{"00c_003", "info", "删除轮播图信息", "info/image/delete", "POST", infoImageController.Delete, ``, ""},
		{"00c_004", "info", "上传轮播图图片", "info/image/upload", "POST", infoImageController.Upload, `{"userId":1,"token":"a","b64":"base64Code","imgType":"jpg"}`, ""},
		{"00d_000", "info", "通知公告列表数据", "info/notice/list", "POST", infoNoticeController.List, ``, ""},
		{"00d_001", "info", "新增通知公告信息", "info/notice/create", "POST", infoNoticeController.Create, ``, ""},
		{"00d_002", "info", "编辑通知公告信息", "info/notice/edit", "POST", infoNoticeController.Edit, ``, ""},
		{"00d_003", "info", "删除通知公告信息", "info/notice/delete", "POST", infoNoticeController.Delete, ``, ""},
		{"00d_004", "info", "通知公告信息回显", "info/notice/echo", "POST", infoNoticeController.Echo, ``, ""},
		{"00d_005", "info", "发送订阅消息", "info/notice/send", "POST", infoNoticeController.Send, `{"userId":0, "token":"", "noticeId":0}`, ""},
		{"00e_000", "student", "已交费列表数据", "student/count/list", "POST", studentCountController.List, ``, ""},
		{"00e_001", "student", "已交费列表数据(返回所有字段)", "student/count/listAll", "POST", studentCountController.ListAll, ``, ""},
		{"00e_002", "student", "交费核实通过", "student/count/allow", "POST", studentCountController.Allow, `{"userId":1,"token":"a","studentId":0,"fee":1000.0}`, ""},
		{"00e_003", "student", "删除学生报名信息", "student/count/delete", "POST", studentCountController.Delete, ``, ""},
		{"00e_004", "student", "交费核实撤销", "student/count/undo", "POST", studentCountController.Undo, `{"userId":1,"token":"a","studentId":0}`, ""},
		{"00f_000", "student", "信息汇总列表数据", "student/gather/list", "POST", studentGatherController.List, ``, ""},
		{"00f_001", "student", "信息汇总列表数据(返回所有字段)", "student/gather/listAll", "POST", studentGatherController.ListAll, ``, ""},
		{"00f_002", "student", "带统计人数的专业列表数据", "student/gather/listSpecial", "POST", studentGatherController.ListSpecial, ``, ""},
		{"00f_003", "student", "调整学生报名信息", "student/gather/edit", "POST", studentGatherController.Edit, `{"userId":0,"token":"","data":{}}`, ""},
		{"00f_004", "student", "删除学生报名信息", "student/gather/delete", "POST", studentGatherController.Delete, ``, ""},
		{"00f_005", "student", "学生退学", "student/gather/exit", "POST", studentGatherController.Exit, `{"userId":0,"token":"","ids":""}`, ""},
		{"00f_006", "student", "撤销退学", "student/gather/exitUndo", "POST", studentGatherController.ExitUndo, `{"userId":0,"token":"","studentId":0}`, ""},
		{"010_000", "student", "学生列表数据", "student/recommend/list", "POST", studentRecommendController.List, ``, ""},
		{"010_001", "student", "推荐教师列表数据", "student/recommend/listTeacher", "POST", studentRecommendController.ListTeacher, ``, ""},
		{"011_000", "student", "学生列表数据", "student/sum/list", "POST", studentSumController.List, `{"deptId":0, "sum":0}`, ""},
		{"011_001", "student", "带统计人数的系部树形数据", "student/sum/tree", "POST", studentSumController.Tree, ``, ""},
		{"012_000", "student", "报名审核列表数据", "student/verify/list", "POST", studentVerifyController.List, ``, ""},
		{"012_001", "student", "报名审核列表数据(返回所有字段)", "student/verify/listAll", "POST", studentVerifyController.ListAll, ``, ""},
		{"012_002", "student", "带统计人数的专业列表数据", "student/verify/listSpecial", "POST", studentVerifyController.ListSpecial, ``, ""},
		{"012_003", "student", "学生信息回显", "student/verify/echo", "POST", studentVerifyController.Echo, ``, ""},
		{"012_004", "student", "报名审核通过", "student/verify/allow", "POST", studentVerifyController.Allow, `{"userId":0,"token":"","studentId":0,"feedback":""}`, ""},
		{"012_005", "student", "报名审核拒绝", "student/verify/deny", "POST", studentVerifyController.Deny, `{"userId":0,"token":"","studentId":0,"feedback":""}`, ""},
		{"012_006", "student", "调整学生报名信息", "student/verify/edit", "POST", studentVerifyController.Edit, `{"userId":0,"token":"","data":{}}`, ""},
		{"012_007", "student", "删除学生报名信息", "student/verify/delete", "POST", studentVerifyController.Delete, ``, ""},
		{"012_008", "student", "提交用户报名信息(后台代报)", "student/verify/create", "POST", studentVerifyController.Create, `{"userId":0,"token":"","data":{}}`, ""},
		{"012_009", "student", "上传用户相关图片(后台代报)", "student/verify/upload", "POST", studentVerifyController.Upload, `{"userId":0,"token":"","b64":"base64Code", "b64sm":"base64Code", "imgType":"jpg", "fieldName":"picture"}`, ""},
		/*系统资源*/
	}

	return initResources
}

// 初始化系统
func init() {
	common.InitConfig()

	if common.Config.RunMode == "dev" {
		logger.SetConsole(true)
	} else {
		logger.SetRollingDaily(common.CurDir+"/logger/", "api.log")
		logger.SetLevel(logger.INFO)
	}

	// 初始化数据库
	orm.Debug = false
	orm.DefaultTimeLoc = time.Local
	err := orm.RegisterDataBase(
		"default",
		"mysql",
		common.Config.Mysql.Url,
		common.Config.Mysql.MaxOpenConns,
		common.Config.Mysql.MaxIdleConns,
	)
	if err != nil {
		logger.Error("连接数据库发生错误：", err)
		return
	}

	err = orm.RunSyncdb("default", false, true)
	if err != nil {
		logger.Error("初始化数据库发生错误：", err)
		return
	}

	logger.Info("初始化数据库成功")

	options := &redis.Options{
		Addr:     common.Config.Redis.Addr,
		Password: common.Config.Redis.Passwd,
		PoolSize: common.Config.Redis.PoolSize,
		DB:       0,
	}
	common.RedisClient = redis.NewClient(options)
	_, err = common.RedisClient.Ping().Result()
	if err != nil {
		logger.Error("连接redis发生错误：", err)
		return
	}

	logger.Info("连接redis成功")

	initData()
}

// 路由分发器
type ServerMux struct {
	SystemResourceMap map[string]http.HandlerFunc
}

// 路由分发
func newServerMux() *ServerMux {
	return &ServerMux{map[string]http.HandlerFunc{}}
}

// 初始化资源
func (mux *ServerMux) InitResource() {
	resources := initRoute()
	for _, res := range resources {
		mux.SystemResourceMap[res.ResUrl+"_"+res.HttpMethod] = res.HttpFunc
	}
}

// 路由分发，权限判断
func (mux *ServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.RequestURI == "/" {
		_, _ = w.Write([]byte("API"))
		return
	}

	origin := r.Header.Get("Origin")
	if origin != "" {
		w.Header().Set("Access-Control-Allow-Origin", common.Config.AllowOrigin)
		w.Header().Set("Access-Control-Allow-Methods", "POST,OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "content-type,X-Requested-With") // 第2个参数是用于接收文件上传
		w.Header().Set("Access-Control-Expose-Headers", "Content-Length,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Content-Type")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
	}

	if r.Method == "OPTIONS" {
		return
	}

	path := r.URL.Path
	path = strings.Trim(path, "/")

	if path == "debug/pprof" {
		pprof.Profile(w, r)
		return
	}

	// 参考自：golang复用http.request.body的方法示例
	// https://www.jb51.net/article/149227.htm
	bodyBytes, _ := ioutil.ReadAll(r.Body) // 把request的内容读取出来
	bodyString := string(bodyBytes)
	r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) // 把刚刚读出来的再写进去
	l := models.Record{
		Path:      path,
		Ip:        r.RemoteAddr,
		AtTime:    time.Now(),
		Method:    r.Method,
		UserAgent: r.Header.Get("User-Agent"),
		Status:    200,
		Params:    bodyString,
	}

	isDanger := common.CheckSQLInject(bodyString, false)
	if isDanger {
		// 检测到了sql注入攻击，予以拦截！
		w.WriteHeader(200)
		w.Header().Set("Content-Type", "application/json;charset=UTF-8")
		_, _ = fmt.Fprint(w, `{"success":false,"msg":"请勿尝试SQL注入！","status":1}`)
		common.LoggerQueue <- l
		fmt.Println(bodyString)
		return
	}

	if r.Method == "GET" {
		common.LoggerQueue <- l
		if strings.Index(path, "upload/") >= 0 {
			// 返回"upload"文件夹里的文件内容
			fixPath := strings.Replace(path, "upload/", "", -1)
			absPath := common.Config.UploadPath // 绝对路径
			url := absPath + fixPath
			http.ServeFile(w, r, url)
			return
		}

		url := "e:/html/" + path
		http.ServeFile(w, r, url)
		return
	}

	httpFunc := mux.SystemResourceMap[path+"_"+r.Method]
	if httpFunc != nil {
		httpFunc(w, r)
		common.LoggerQueue <- l
		return
	}

	w.WriteHeader(404)
	w.Header().Set("Content-Type", "application/json;charset=UTF-8")
	_, _ = fmt.Fprint(w, `{"success":false,"msg":"您访问的该资源不存在","status":1}`)
	l.Status = 404
	common.LoggerQueue <- l
}

// 数据初始化（系统菜单，默认用户，默认角色等）
func initData() {
	var err error
	conn := orm.NewOrm()
	menus := []models.Menu{
		{1, 0, "系统管理", "setting", "basic"},
		{10, 1, "全局设置", "", "globalSet"},
		{11, 1, "系统角色", "", "role"},
		{12, 1, "用户管理", "", "user"},
		{13, 1, "系统资源", "", "resource"},
		{14, 1, "系和专业", "", "special"},

		{2, 0, "学生管理", "usergroup-add", "student"},
		{20, 2, "报名审核", "", "verify"},
		{21, 2, "交费核实", "", "count"},
		{23, 2, "信息汇总", "", "gather"},
		{24, 2, "推荐教师", "", "recommend"},
		{25, 2, "入学成绩", "", "sum"},

		{3, 0, "信息管理", "read", "info"},
		{30, 3, "信息发布", "", "content"},
		{31, 3, "通知公告", "", "notice"},
		{32, 3, "轮播图管理", "", "image"},
	}

	// 初始化菜单
	menuIds := []string{}
	for _, menu := range menus {
		_, err = conn.InsertOrUpdate(&menu)
		if err != nil {
			logger.Error("初始化菜单发生错误：", err)
			continue
		}

		menuIds = append(menuIds, common.ToStr(menu.Id))
	}
	menuStr := strings.Join(menuIds, "','")
	// 去重
	sql := "delete from basic_menu where id not in ('" + menuStr + "')"
	_, err = conn.Raw(sql).Exec()
	if err != nil {
		logger.Error("初始化菜单发生错误：", err)
		return
	}

	// 初始化资源
	resIds := []string{}
	resUrls := []string{}
	resources := initRoute()
	for _, res := range resources {
		_, err := conn.InsertOrUpdate(&res)
		if err != nil {
			logger.Error("初始化资源发生错误：", err)
			continue
		}

		resIds = append(resIds, res.Id)
		resUrls = append(resUrls, res.ResUrl)
	}
	resStr := strings.Join(resIds, "','")
	// 去重
	sql = "delete from basic_resource where id not in ('" + resStr + "')"
	_, err = conn.Raw(sql).Exec()
	if err != nil {
		logger.Error("初始化资源发生错误：", err)
		return
	}

	// 预置系统角色
	count := 0
	sql = "select count(1) from basic_role where name = ?"
	err = conn.Raw(sql, "超级管理员").QueryRow(&count)
	if err != nil {
		logger.Error("预置系统角色发生错误：", err)
		return
	}

	menuStr = strings.Join(menuIds, ",")
	resStr = strings.Join(resUrls, ",")
	role := &models.Role{Id: 1, Name: "超级管理员", Menus: menuStr, Res: resStr}
	if count == 0 {
		_, err = conn.Insert(role)
	} else {
		_, err = conn.Update(role)
	}
	if err != nil {
		logger.Error("预置系统角色发生错误：", err)
		return
	}

	// 预置系统管理员
	sql = "select count(1) from basic_user where user_name = ?"
	err = conn.Raw(sql, "admin").QueryRow(&count)
	if err != nil {
		logger.Error("预置系统管理员发生错误：", err)
		return
	}

	if count == 0 {
		user := &models.User{
			Id:       1,
			UserName: "admin",
			Passwd:   "e10adc3949ba59abbe56e057f20f883e",
			Name:     "超级管理员",
			Phone:    "15234579277",
			Status:   true,
			Roles:    "1",
		}
		_, err = conn.Insert(user)
		if err != nil {
			logger.Error("预置系统管理员发生错误：", err)
			return
		}
	}

	sql = "select count(1) from basic_global_set where `name` = ?"
	err = conn.Raw(sql, "showPictureUpload").QueryRow(&count)
	if err != nil {
		logger.Error(err)
		return
	}

	if count == 0 {
		set := &models.GlobalSet{
			Name:  "showPictureUpload",
			Value: "true",
			Label: "是否显示“免冠证件照”上传按钮",
		}
		_, err = conn.Insert(set)
		if err != nil {
			logger.Error(err)
			return
		}
	}

	err = conn.Raw(sql, "signUpTimeFrame").QueryRow(&count)
	if err != nil {
		logger.Error(err)
		return
	}

	if count == 0 {
		set := &models.GlobalSet{
			Name:  "signUpTimeFrame",
			Value: "07-01,08-31",
			Label: "报名时间段",
		}
		_, err = conn.Insert(set)
		if err != nil {
			logger.Error(err)
			return
		}
	}
}

// Windows服务结构体定义
type AdminProgram struct{}

// 启动服务
func (p *AdminProgram) Start(service.Service) error {
	go p.run()
	return nil
}

// 程序运行
func (p *AdminProgram) run() {
	defer common.PanicHandler()

	go common.LoggerQueueToDb() // 日志入库协程

	logger.Info("监听端口：", common.Config.Port)

	serverMux := newServerMux()
	serverMux.InitResource()

	var err error
	if common.Config.Port >= 443 {
		err = http.ListenAndServeTLS(
			":"+common.ToStr(common.Config.Port),
			common.Config.Cert.Crt,
			common.Config.Cert.Key,
			serverMux,
		)
	} else {
		err = http.ListenAndServe(":"+common.ToStr(common.Config.Port), serverMux)
	}
	if err != nil {
		logger.Error("监听发生错误：", err)
	}
}

// 停止服务
func (p *AdminProgram) Stop(service.Service) error {
	return nil
}

// 程序启动入口
func main() {
	defer common.PanicHandler()
	apiName := common.Config.ApiName
	config := &service.Config{
		Name:        apiName,
		DisplayName: apiName,
		Description: common.Config.Title + "服务",
	}

	prg := &AdminProgram{}
	s, err := service.New(prg, config)
	if err != nil {
		log.Fatal(err)
	}

	if len(os.Args) > 1 {
		if os.Args[1] == "install" {
			err = s.Install()
			if err != nil {
				log.Fatal(apiName+"服务安装失败", err)
			} else {
				log.Println(apiName + "服务安装成功")
			}
			return
		}

		if os.Args[1] == "remove" {
			err = s.Uninstall()
			if err != nil {
				log.Fatal(apiName+"服务卸载失败", err)
			} else {
				log.Println(apiName + "服务卸载成功")
			}
			return
		}
	}

	err = s.Run()
	if err != nil {
		log.Fatal(apiName+"服务运行失败", err)
	}
}
